//#########################################################################
//                              WackiEngie
//           Copyright (c) 2014 Marc Wacker. All rights reserved.
//#########################################################################

#pragma once

#include "WeEngine.h"
#include "WeModule.h"
#include "WeEvent.h"
#include "WeInputEvents.h"
#include "WeDebug.h"


namespace WackiEngine
{
	// case insensitive strings for fast comparisons
	// TODO: better name for this
	class FName
	{
	public:
		FName();
		FName(const String& str);

		bool operator==(const FName& rhs) const {
			return (_id == rhs._id);
		}

	private:
		uint32	_id;
		static Map<String, uint32> _uniqueIds;
		static uint32 _nextId;
	};

	// key combination that can be mapped to an action
	struct ActionMapping
	{
		KeyCode key;
		bool shift;
		bool ctrl;
		bool alt;

		ActionMapping(KeyCode key, bool shift, bool ctrl, bool alt = false)
			: key(key), shift(shift), ctrl(ctrl), alt(alt)
		{ }

		bool operator== (const ActionMapping& rhs) const {
			return key == rhs.key
				&& shift == rhs.shift
				&& ctrl == rhs.ctrl
				&& alt == rhs.alt;
		}
		bool operator< (const ActionMapping& rhs) const {
			return key < rhs.key;
		}
	};

	// axis mapping
	struct AxisMapping
	{
		KeyCode key;
		float scale;
		bool invert;

		AxisMapping(KeyCode key, float scale = 1.0f, bool invert = false)
			: key(key), scale(scale), invert(invert)
		{ }
		
		bool operator== (const AxisMapping& rhs) const {
			return key == rhs.key;
		}

		bool operator< (const AxisMapping& rhs) const {
			return key < rhs.key;
		}
	};

	struct ActionBinding
	{
		// unique name of this action
		FName name;

		// an action works like a key, so it has a down state
		bool isDown;
		
		// signals for this event, the user can subscribe to any of the KeyEventTypes of an action (press, release, doubleclick, repeat)
		Event<void(uint32)> signal[KE_Count];

		Set<ActionMapping> mappings;
	};


	struct AxisBinding
	{
		// unique name of this axis
		FName name;

		// current value of this virtual axis
		// this value is the sum of all of its affecting axes for the current frame
		float value;

		// the signal that can be subscribed to for this virtual axis
		Event<void(float, uint32)> signal;

		Set<AxisMapping> mappings;
	};

	struct ActionEvent
	{
		ActionEvent(ActionBinding* ab, KeyEventType et, uint32 did)
		: actionBinding(ab), eventType(et), deviceId(did)
		{ }

		ActionBinding*	actionBinding;
		KeyEventType	eventType;
		uint32			deviceId;
	};

	/*
	
	TODO: 
		1. implement the basics and test if actions fire
			- keystatemap tracks the keystates for the currentframe
				-for axis go through all the axisbindings and sum up the mappings values into axisbinding.value
					- after traversing mappings fire the signal.
				- for actions fire the signal the number of times the different events occured (only if the events have registered callbacks)

		2. make sure the Set<Action> and AxisMapping only contain unique entries
			- implement == operator and other needed stuff for std::set to work.
				
	*/
	

	class VirtualInput : public Module<VirtualInput>
	{
	public:
		VirtualInput();

		// fire all active axis events continuously here
		void update();


		// create a new action mapping
		void addAction(const String& name);

		void removeAction(const String& name);

		// create a new virtual axis
		void addAxis(const String& name, bool inverted = false);

		void removeAxis(const String& name);


		// add a key to a virtual action mapping
		void addActionMapping(const String& actionName, KeyCode key, bool shift = false, bool ctrl = false, bool alt = false);

		// add an axis to a virtual axis mapping
		void addAxisMapping(const String& axisName, KeyCode key, float scale = 1.0f);


		// bind function "func" of class c as a callback for the action mapping "name" and trigger on event type "type"
		template<class C>
		void bindAction(const String& name, KeyEventType type, C* c, void (C::*func)(uint32));

		// bind function "func" of class c as a callback for the axis mapping "name"
		template<class C>
		void bindAxis(const String& name, C* c, void (C::*func)(float, uint32));



	protected:
		void onKey(const KeyEvent& e);
		void onAxis(const AxisEvent& e);

		// helper functions to access the action and axis bindings
		// searches for an action binding by name, returns nullptr if it doesnt exist
		ActionBinding* findActionBinding(const String& name);

		// searches for an axis binding by name, returns nullptr if it doesnt exist
		AxisBinding* findAxisBinding(const String& name);

	private:
		// unique action and axis bindings
		Vector<ActionBinding>	_actionBindings;
		Vector<AxisBinding>		_axisBindings;

		Map<KeyCode, Vector<ActionBinding*>> _keyActionReleationships;
		Vector<ActionEvent>		_actionQueue;
	};

	// template implementations here

	// bind function "func" of class c as a callback for the action mapping "name" and trigger on event type "type"
	template<class C>
	void VirtualInput::bindAction(const String& name, KeyEventType type, C* c, void (C::*func)(uint32))
	{
		ActionBinding* binding = findActionBinding(name);
		if(binding == nullptr) {
			throw "Trying to bind a non existant action!"; // TODO: add better exception handling
		}

		// subscribe to the event
		binding->signal[type].connect(c, func);
	}

	// bind function "func" of class c as a callback for the axis mapping "name"
	template<class C>
	void VirtualInput::bindAxis(const String& name, C* c, void (C::*func)(float, uint32))
	{
		AxisBinding* binding = findAxisBinding(name);
		if(binding == nullptr) {
			throw "Trying to bind a non existant axis!"; // TODO: add better exception handling
		}

		// subscribe to the event
		binding->signal.connect(c, func);
	}

	VirtualInput& gVirtualInput();

}




/*



Idea for input handling a la ue4:


Problem: how do we receive virtual input everywhere else?

Q: how do you realize a main menu for example?
	A: check out ue4 gamemodes and what they do in that engine, they are used for main menus etc and somehow switch input around.

Q: Would it be enough that only classes with aplayerinput component receive virtual input?

get the sourcecode of shoodergame and check out who switches game modes...

if nothing works just do everything virtual input related in here and make sure to consume events after the first one received it

thats still not satisfying, we have to do something that posesses something else that then receives player input, that would be nicer

// maps a key to an action name (FName)
InputActionKeyMapping



ActionBinding
name
eventType
consume
signal

AxisBinding
name
value
signal




ActionMapping
name
shift, alt, ctrl etc...
key

AxisMapping
name
invert
scale
key


KeyDetails
Array<ActionMapping>

axisDetails
Array<AxisMapping>



KeyState
value
EventCount	//press release etc...


PlayerInput:
devideId	// the deviceId this playerinput component receives its input from
Map<ActionName, KeyDetails>
Map<AxisName, AxisDetails>
Map<Keycode, KeyState>


keyInput() {
if press
keystatemap[key].value = 1.0
if release
keystatemap[key].value = 0.0

// count event occurances here
}

axisChanged()
{
keystatemap[key].value = value

// additionaly check a threshold here and count press, release events?
}


update()
{

1. go through all ActionBindings
{
1. find the corresponding keydetails in the actionmap
2. go through each key and check if the wanted event has occured
3. check the consumed key map and fire the event if it wasnt consumed already
4. consume key if the map tells us to
5. fire the signal
}
2. go through axisBindings
{
1. find corresponding axisdetails in the axismap
2. reset the value parameer of the binding
3. go through each axis in the map and add it to the valeu parameter
4. fire the signal with the value
}


}




*/